/**
 * 
 */
package com.massyframework.beanskin.graphql.subscription;

import static graphql.execution.FieldCollectorParameters.newParameters;

import java.util.concurrent.CompletableFuture;

import com.massyframework.beanskin.graphql.fetching.CustomizeField;
import com.massyframework.beanskin.graphql.fetching.MergedFieldFactory;
import com.massyframework.beanskin.graphql.util.GraphQLContextUtils;

import graphql.ExecutionResult;
import graphql.execution.DataFetcherExceptionHandler;
import graphql.execution.ExecutionContext;
import graphql.execution.ExecutionStepInfo;
import graphql.execution.ExecutionStrategyParameters;
import graphql.execution.FieldCollectorParameters;
import graphql.execution.MergedField;
import graphql.execution.MergedSelectionSet;
import graphql.execution.NonNullableFieldValidator;
import graphql.execution.SubscriptionExecutionStrategy;
import graphql.schema.GraphQLObjectType;

/**
 * {@link SubscriptionExecutionStrategyEx},扩展SubjectscriptionExecutionStrategy能力，支持不固定的事件类型
 * @author huangkh
 *
 */
public class SubscriptionExecutionStrategyEx extends SubscriptionExecutionStrategy {
	


	/**
	 * 
	 */
	public SubscriptionExecutionStrategyEx() {
	}

	/**
	 * @param dataFetcherExceptionHandler
	 */
	public SubscriptionExecutionStrategyEx(DataFetcherExceptionHandler dataFetcherExceptionHandler) {
		super(dataFetcherExceptionHandler);
	}


    /**
     * Called to turn an java object value into an graphql object value
     *
     * @param executionContext   contains the top level execution parameters
     * @param parameters         contains the parameters holding the fields to be executed and source object
     * @param resolvedObjectType the resolved object type
     * @param result             the result to be coerced
     *
     * @return a promise to an {@link ExecutionResult}
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
    protected CompletableFuture<ExecutionResult> completeValueForObject(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLObjectType resolvedObjectType, Object result) {
        if (result != null){
            MergedFieldFactory factory =  null;
            if (parameters.getField().getSingleField() instanceof CustomizeField){
                factory = ((CustomizeField)parameters.getField().getSingleField()).getMergedFieldFactory();
            }else {
                String path = parameters.getPath().toString();
                factory = GraphQLContextUtils.removeAttribute(executionContext.getContext(), path, MergedFieldFactory.class);
            }


            if (factory != null){
                MergedField field = factory.createMargedField(executionContext, parameters,
                        result, resolvedObjectType);

                parameters = ExecutionStrategyParameters.newParameters(parameters)
                        .field(field)
                        .build();

            }
        }

        ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo();

        FieldCollectorParameters collectorParameters = newParameters()
                .schema(executionContext.getGraphQLSchema())
                .objectType(resolvedObjectType)
                .fragments(executionContext.getFragmentsByName())
                .variables(executionContext.getVariables())
                .build();


        MergedSelectionSet subFields = fieldCollector.collectFields(collectorParameters, parameters.getField());

        ExecutionStepInfo newExecutionStepInfo = executionStepInfo.changeTypeWithPreservedNonNull(resolvedObjectType);
        NonNullableFieldValidator nonNullableFieldValidator = new NonNullableFieldValidator(executionContext, newExecutionStepInfo);

        ExecutionStrategyParameters newParameters = parameters.transform(builder ->
                builder.executionStepInfo(newExecutionStepInfo)
                        .fields(subFields)
                        .nonNullFieldValidator(nonNullableFieldValidator)
                        .source(result)
        );



        // Calling this from the executionContext to ensure we shift back from mutation strategy to the query strategy.

        return executionContext.getQueryStrategy().execute(executionContext, newParameters);
    }

}
